package it.antonio.ilpizze.func.util;

import it.antonio.ilpizze.func.ComputeIterator;
import it.antonio.ilpizze.func.Mapper;
import it.antonio.ilpizze.func.Order;
import it.antonio.ilpizze.func.Predicate;

import java.util.AbstractList;
import java.util.AbstractSequentialList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

public class InternalUtils {
	public static <T> Comparator<? super T> comparator(final Order<T> order) {
		return new Comparator<T>() {

			@Override
			public int compare(T e1, T e2) {
				int result = order.order(e1, e2);
				switch (result) {
					case Order.EQUALS: return 0;
					case Order.FIRST_GREATER: return 1;
					case Order.SECOND_GREATER: return -1;
				}
				throw new IllegalStateException("Cannot decode order");
			}
		};
	}
	
	public static <T> AbstractList<T> predicateRandomAccessList(final List<T> delegate, final Predicate<? super T> pred){
		return new AbstractList<T>() {

			@Override
			public T get(int index) {
				T elem = delegate.get(index);
				if(pred.match(elem)) return elem;
				return null;
			}

			@Override
			public int size() {
				return delegate.size();
			}
		};
	}
	public static <T> AbstractSequentialList<T> predicateSequentialList(final List<T> delegate, final Predicate<? super T> pred){
		return new AbstractSequentialList<T>() {
			@Override
			public int size() {
				return delegate.size();
			}

			@Override
			public ListIterator<T> listIterator(int index) {
				final ListIterator<T> lit = delegate.listIterator(index);
				final Iterator<T> nextIt = new ComputeIterator<T>() {

					@Override
					protected T computeNext() {
						if(lit.hasNext()) {
							T next = lit.next();
							if(pred.match(next)){
								return next;
							} else {
								return computeNext();
							}
						}
						return finished();
					}
				};
				
				final Iterator<T> prevIt = new ComputeIterator<T>() {

					@Override
					protected T computeNext() {
						if(lit.hasPrevious()) {
							T previous = lit.previous();
							if(pred.match(previous)){
								return previous;
							} else {
								return computeNext();
							}
						}
						return finished();
					}
				};
				return new ImmutableListIterator<T>() {

					@Override
					public boolean hasNext() {
						return nextIt.hasNext();
					}

					@Override
					public boolean hasPrevious() {
						return prevIt.hasNext();
					}

					@Override
					public T next() {
						return nextIt.next();
					}

					@Override
					public int nextIndex() {
						return lit.nextIndex();
					}

					@Override
					public T previous() {
						return prevIt.next();
					}

					@Override
					public int previousIndex() {
						return lit.previousIndex();
					}
				};
			}
		};
	}
	
	
	public static <FROM, TO> AbstractList<TO> mappedRandomAccessList(final List<FROM> delegate,final Mapper<? super FROM, ? extends TO> mapper){
		return new AbstractList<TO>() {
			@Override
			public TO get(int index) {
				return mapper.map(delegate.get(index));
			}

			@Override
			public int size() {
				return delegate.size();
			}
		};
	}
	public static <FROM, TO> AbstractSequentialList<TO> mappedSequentialList(final List<FROM> delegate,final Mapper<? super FROM, ? extends TO> mapper){
		return new AbstractSequentialList<TO>() {
			@Override
			public ListIterator<TO> listIterator(int index) {
				final ListIterator<FROM> lit = delegate.listIterator(index);
				return new ImmutableListIterator<TO>() {

					@Override
					public boolean hasNext() {
						return lit.hasNext();
					}

					@Override
					public boolean hasPrevious() {
						return lit.hasPrevious();
					}

					@Override
					public TO next() {
						return mapper.map(lit.next());
					}

					@Override
					public int nextIndex() {
						return lit.nextIndex();
					}

					@Override
					public TO previous() {
						return mapper.map(lit.previous());
					}

					@Override
					public int previousIndex() {
						return lit.previousIndex();
					}

					
				};
			}

			@Override
			public int size() {
				return delegate.size();
			}
		};
	}
	
	public static <T> AbstractList<T> cycleRandomAccessList(final List<T> delegate){
		return new AbstractList<T>() {

			@Override
			public T get(int index) {
				return delegate.get(index % delegate.size());
			}

			@Override
			public int size() {
				throw new UnsupportedOperationException("Cycle List");
			}
		};
	}
	public static <T> AbstractSequentialList<T> cycleSequentialList(final List<T> delegate){
		return new AbstractSequentialList<T>() {
			@Override
			public ListIterator<T> listIterator(final int index) {
				return new ImmutableListIterator<T>() {
					
					ListIterator<T> lit = delegate.listIterator(index);
					
					private void checkNext(){
						if(!lit.hasNext()){
							lit = delegate.listIterator();
						}
					}
					private void checkPrev(){
						if(!lit.hasNext()){
							lit = delegate.listIterator(delegate.size() - 1);
						}
					}
					
					@Override
					public boolean hasNext() {
						checkNext();
						return true;
					}

					@Override
					public boolean hasPrevious() {
						checkPrev();
						return true;
					}

					@Override
					public T next() {
						checkNext();
						return lit.next();
					}

					@Override
					public int nextIndex() {
						checkNext();
						return lit.nextIndex();
					}

					@Override
					public T previous() {
						checkPrev();
						return lit.previous();
					}

					@Override
					public int previousIndex() {
						checkPrev();
						return lit.previousIndex();
					}

				};
			}

			@Override
			public int size() {
				return delegate.size();
			}

		};
	}
	
	public static <T> AbstractList<T> truncateRandomAccessList(final List<T> delegate, final int size){
		return new AbstractList<T>() {

			@Override
			public T get(int index) {
				if(index < size()){
					return delegate.get(index);
				}
				throw new IndexOutOfBoundsException("Index: " + index);
			}

			@Override
			public int size() {
				int dSize = delegate.size();
				return dSize < size ? dSize : size;
			}
		};
	}
	public static <T> AbstractSequentialList<T> truncateSequentialList(final List<T> delegate, final int size){
		return new AbstractSequentialList<T>() {
			@Override
			public ListIterator<T> listIterator(final int index) {
				final ListIterator<T> lit = delegate.listIterator(index);
				return new ImmutableListIterator<T>() {

					int pos = index;
					
					@Override
					public boolean hasNext() {
						return pos < size && lit.hasNext();
					}

					@Override
					public boolean hasPrevious() {
						return lit.hasPrevious();
					}

					@Override
					public T next() {
						if(pos < size){
							return lit.next();
						}
						throw new NoSuchElementException();
					}

					@Override
					public int nextIndex() {
						return lit.nextIndex();
					}

					@Override
					public T previous() {
						return lit.previous();
					}

					@Override
					public int previousIndex() {
						return lit.previousIndex();
					}

				};
			}

			@Override
			public int size() {
				int dSize = delegate.size();
				return dSize < size ? dSize : size;
			}

		};
	}
	
	public static <T> AbstractList<T> concatRandomAccessList(final List<T> first, final List<T> second){
		return new AbstractList<T>() {

			@Override
			public int size() {
				return first.size() + second.size();
			}

			
			@Override
			public T get(int index) {
				if(index < first.size()) return first.get(index);
				return second.get(index);
			}

		};
	}
	public static <T> AbstractSequentialList<T> concatSequentialList(final List<T> first, final List<T> second){
		return new AbstractSequentialList<T>() {
			@Override
			public int size() {
				return first.size() + second.size();
			}

			@Override
			public ListIterator<T> listIterator(final int index) {
				return new ConcatListIterator<T>(index, first, second);
			}


		};
	}
	
	private static class ConcatListIterator<T> extends ImmutableListIterator<T>{
		private List<T> first;
		private List<T> second;
		
		private boolean isFirst;
		private ListIterator<T> lit;
		
		public ConcatListIterator(int index, List<T> first, List<T> second) {
			super();
			this.first = first;
			this.second = second;
			isFirst = (index < first.size());
			lit = isFirst ? first.listIterator(index) : second.listIterator(index);
		}
		
		private void checkNext(){
			if(!lit.hasNext() && isFirst){
				lit = second.listIterator();
				isFirst = false;
			}
		}
		private void checkPrev(){
			if(!lit.hasPrevious() && !isFirst){
				lit = first.listIterator(first.size());
				isFirst = true;
			}
		}
		
		@Override
		public boolean hasNext() {
			checkNext();
			return lit.hasNext();
		}
		@Override
		public boolean hasPrevious() {
			checkPrev();
			return lit.hasPrevious();
		}
		@Override
		public T next() {
			checkNext();
			return lit.next();
		}
		@Override
		public int nextIndex() {
			checkNext();
			return lit.nextIndex();
		}
		@Override
		public T previous() {
			checkPrev();
			return lit.previous();
		}
		@Override
		public int previousIndex() {
			checkPrev();
			return lit.previousIndex();
		}
		
	}
}
